package kuzmenkov.servlet;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * The class is responsible for finding screen files and sending the list of
 * them to client
 * 
 * @author Michael Kuzmenkov
 * 
 */
public class ViewerServlet extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 505653301820044665L;

	private Logger log = Logger.getLogger(this.getClass().getPackage()
			.getName());

	private String dirOfScreens = "screens";

	private int periodOfScan = 300000;

	/**
	 * When the last search of the screen files was
	 */
	private long prevScanTime = 0L;
	/**
	 * The result of the last search of the screen files
	 */
	private String prevResponse;

	private static final String EXT = "xml";
	/**
	 * The name of the context parameter which holds the directory with XML
	 * screen files
	 */
	private static final String DIR_OF_SCREENS_PARAMETER = "dirOfScreens";
	/**
	 * The URL pattern to invoke the servlet
	 */
	public static final String SERVLET_URL_PATTERN = "ViewerServlet";

	/**
	 * Delimits paths to screen files
	 */
	private static final Object DELIMITER = ";";

	private static final String PERIOD_OF_SCAN_PARAMETER = "periodOfScan";

	@Override
	public void init() throws ServletException {

		ServletConfig sc = getServletConfig();
		if (sc == null) {
			log.warning("Getting of the init parameters failed. "
					+ "The default values will be used");
			return;
		}

		String dirOfScreensValue = sc
				.getInitParameter(DIR_OF_SCREENS_PARAMETER);
		if (dirOfScreensValue == null)
			log.warning("The parameter " + DIR_OF_SCREENS_PARAMETER
					+ " was not found. The default value will be used");
		else if (dirOfScreensValue.isEmpty())
			log.warning("The value of the parameter "
					+ DIR_OF_SCREENS_PARAMETER
					+ " is empty. The default value will be used");
		else
			dirOfScreens = dirOfScreensValue;
		log.fine("dirOfScreens=" + dirOfScreens);

		String periodOfScanValue = sc
				.getInitParameter(PERIOD_OF_SCAN_PARAMETER);
		if (periodOfScanValue == null)
			log.warning("The parameter " + PERIOD_OF_SCAN_PARAMETER
					+ " was not found. The default value will be used");
		else {
			try {
				periodOfScan = Integer.parseInt(periodOfScanValue);
			} catch (NumberFormatException e) {
				log.warning("The value of the parameter "
						+ PERIOD_OF_SCAN_PARAMETER
						+ " is not correct. It must be integer. The default value will be used");
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		String response = null;
		long time = System.currentTimeMillis();
		if (time - prevScanTime < periodOfScan) {
			log.fine("The previous response will  be returned");
			response = prevResponse;

		} else {
			log.fine("The new response will be returned");
			try {
				File[] files = findXMLScreens(dirOfScreens);
				if (files == null) {
					log.warning("No XML screen files was found!");
					return;
				}

				response = formResponse(files);
				log.fine("response=" + response);
			} catch (Exception e) {
				log.severe("While creating the response error occured: " + e.getMessage());
				e.printStackTrace();
				return;
			}
			prevScanTime = time;
			prevResponse = response;
		}

		resp.setContentType("text/html");
		PrintWriter writer = resp.getWriter();
		writer.println(response);
		writer.close();
	}

	/**
	 * Forms the response containing paths to the screen files
	 * 
	 * @param files
	 *            the screen files. A precondition: files != null
	 * @return the response
	 */
	private String formResponse(File[] files) {
		StringBuffer response = new StringBuffer();
		for (int i = 0; i < files.length; i++) {
			if (i > 0)
				response.append(DELIMITER);
			File file = files[i];
			response.append(dirOfScreens + "/" + file.getName());
		}
		return response.toString();
	}

	/**
	 * Finds screen files by path
	 * 
	 * @param path
	 *            the path to the directory with the screen files
	 * @return the array of the files
	 * @throws Exception
	 */
	private File[] findXMLScreens(String path) throws Exception {
		File dir = new File(getServletContext().getRealPath(path));
		log.fine("dir=" + dir.getAbsolutePath());

		if (!dir.exists() || !dir.isDirectory()) {
			throw new Exception(
					"The directory with screen files doesn't exist. "
							+ "Check the param 'dirOfScreens' in the file web.xml");
		}

		FilenameFilter filter = new XMLFileNameFilter();
		File[] files = dir.listFiles(filter);
		if (files == null) {
			throw new Exception(
					"Scanning for the screen files failed. Maybe an I/O error was");
		}
		log.fine(files.length + " screen files were found");

		return files;
	}

	class XMLFileNameFilter implements FilenameFilter {

		@Override
		public boolean accept(File dir, String name) {
			return name != null
					&& name.toLowerCase().endsWith(EXT.toLowerCase());
		}

	}

}
